package org.example.chat.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.example.chat.websocket.nettyhandler.NoHeartBeatHandler;
import org.example.chat.websocket.nettyhandler.TextWebSocketFrameHandler;
import org.example.chat.config.ServerConfigProperties;
import org.example.chat.utils.CommonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author cheval
  */
@Slf4j
@Component
public class WSServer {

    private boolean isStarted = false;
    @Autowired
    private ServerConfigProperties.WsProperties wsProperties;
    /** 文本帧处理器*/
    @Autowired
    TextWebSocketFrameHandler textWebSocketFrameHandler;
    /** 无心跳时的处理器 */
    @Autowired
    NoHeartBeatHandler noHeartBeatHandler;

    public synchronized void start() {
        if (isStarted) {
            return;
        }
        isStarted = true;
        // 1. 创建服务启动器
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 2. 配置服务启动器
        // 2.1 配置事件循环组， boss 负责连接 worker负责读写消息
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        bootstrap.group(boss, worker);
        // 2.2 选择serverSocketChannel的实现为NIO
        bootstrap.channel(NioServerSocketChannel.class);
        // 2.3 开启全局日志处理器（即打印boss日志也打印worker日志）
        bootstrap.handler(new LoggingHandler());
        // 2.4 配置worker的处理器
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                ChannelPipeline pipeline = socketChannel.pipeline();
                // 间隔5s收不到心跳消息，就会触发IdleStateEvent事件
                pipeline.addLast(new IdleStateHandler(5,0,0));
                // 心跳处理器
                pipeline.addLast(noHeartBeatHandler);
                // 使用http消息编码解码器
                pipeline.addLast(new HttpServerCodec());
                // 以块的方式向channel写消息，添加ChunkedWriteHandler()
                pipeline.addLast(new ChunkedWriteHandler());
                // HttpObjectAggregator可以将http分段的数据报聚合起来
                pipeline.addLast(new HttpObjectAggregator(1024 * 60));
                // 对于websocket的数据是以帧的形式传递的,将http协议升级为ws协议并注册ws服务端点
                // ws://localhost:8080/ws
                pipeline.addLast(new WebSocketServerProtocolHandler(wsProperties.getEndpoint(), null, true, 1024 * 60, true, true, 10000L));
                // 自定义handler,处理业务逻辑，以帧的形式处理
                pipeline.addLast(textWebSocketFrameHandler);
            }
        });
        // 2.5 绑定端口
        try {
            ChannelFuture channelFuture = bootstrap.bind(wsProperties.getPort()).sync();
            log.info("websocket服务启动成功， 地址 =====> {}", "ws://" + CommonUtils.getServerIp() + ":" +  wsProperties.getPort() + wsProperties.getEndpoint());
            channelFuture.channel().closeFuture().sync();
            log.info("websocket服务已经停止");
        } catch (Exception e) {
            log.error("websocket服务异常， 异常信息为 ===========> {}", e);
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
